<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tower of Hanoi</title>
    <script src="js/tailwindcss.js"></script>
    <link href="css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#3B82F6',
                        secondary: '#10B981',
                        danger: '#EF4444',
                        warning: '#F59E0B',
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .disk-shadow {
                box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3);
            }
            .tower-base {
                background-color: #9CA3AF;
            }
            .tower-rod {
                background-color: #6B7280;
            }
        }
    </style>
</head>
<body class="bg-gray-50 font-sans">
    <div class="container mx-auto px-4 py-8 max-w-6xl">
        <header class="mb-8">
            <h1 class="text-4xl font-bold text-gray-800 mb-2">汉诺塔</h1>
            <p class="text-gray-600">一款经典的数学谜题游戏</p>
        </header>

        <main id="hanoi-app" class="bg-white rounded-lg shadow-lg p-4 md:p-6">
            <!-- Game content will be inserted here by React -->
        </main>

        <section class="mt-8 bg-white rounded-lg shadow-lg p-6">
            <h2 class="text-2xl font-bold text-gray-800 mb-4">游戏规则</h2>
            <div class="prose max-w-none">
                <p>汉诺塔是一款经典谜题，规则如下:</p>
                <ol class="list-decimal pl-5 my-4">
                    <li>1. 每次只能移动一个圆盘</li>
                    <li>2. 不能将较大的圆盘放在较小的圆盘上方</li>
                </ol>
                <p>目标是用最少的步数，将所有圆盘从 A 塔（第一根柱子）移动到 C 塔（第三根柱子）。</p>
            </div>
        </section>
    </div>

    <script src="js/react.development.js"></script>
    <script src="js/react-dom.development.js"></script>
    <script src="js/babel.min.js"></script>

    <script type="text/babel">
        const { useState, useEffect, useCallback, useMemo } = React;

        // Disk color palette
        const DISK_COLORS = [
            "#FF6B6B", "#4ECDC4", "#45B7D1", "#FFA07A", "#98D8C8", 
            "#FFD700", "#FF69B4", "#20B2AA", "#8A2BE2", "#00FA9A", 
            "#FF4500", "#1E90FF", "#FF1493", "#32CD32", "#FF8C00", "#4169E1"
        ];

        const DISK_HEIGHT = 20;
        const MAX_DISKS = 30;

        // Simple i18n implementation
        const translations = {
            en: {
                settings: "Settings",
                numberOfDisks: "Number of Disks",
                speed: "Speed",
                mode: "Mode",
                manual: "Manual",
                auto: "Auto",
                start: "Start",
                continue: "Continue",
                getHint: "Get Hint",
                reset: "Reset",
                movesCount: "Total: {{total}} / Minimum: {{minimum}}",
                moveSuccess: "Move successful!",
                moveFailure: "Move failed. Larger disk cannot be placed on smaller disk.",
                gameCompleted: "Congratulations! You completed the game!",
                hintMessage: "Hint: Move from tower {{from}} to tower {{to}}",
                noHintAvailable: "No hint available. You've completed the puzzle!"
            },
            zh: {
                settings: "设置",
                numberOfDisks: "盘子数量",
                speed: "速度",
                mode: "模式",
                manual: "手动",
                auto: "自动",
                start: "开始",
                continue: "继续",
                getHint: "获取提示",
                reset: "重置",
                movesCount: "总步数: {{total}} / 最少步数: {{minimum}}",
                moveSuccess: "移动成功！",
                moveFailure: "移动失败。大盘不能放在小盘上面。",
                gameCompleted: "恭喜！你完成了游戏！",
                hintMessage: "提示：从 {{from}} 塔移动到 {{to}} 塔",
                noHintAvailable: "没有可用提示。你已经完成了谜题！"
            }
        };

        // Custom Listbox component
        const CustomListbox = ({ value, onChange, options, disabled }) => {
            const [isOpen, setIsOpen] = useState(false);
            const [selectedValue, setSelectedValue] = useState(value);

            useEffect(() => {
                setSelectedValue(value);
            }, [value]);

            const toggleListbox = () => {
                if (!disabled) setIsOpen(!isOpen);
            };

            const handleOptionClick = (option) => {
                setSelectedValue(option);
                onChange(option);
                setIsOpen(false);
            };

            // Close listbox when clicking outside
            useEffect(() => {
                const handleClickOutside = (event) => {
                    const listbox = document.getElementById('custom-listbox');
                    if (listbox && !listbox.contains(event.target)) {
                        setIsOpen(false);
                    }
                };

                document.addEventListener('mousedown', handleClickOutside);
                return () => {
                    document.removeEventListener('mousedown', handleClickOutside);
                };
            }, []);

            return (
                <div id="custom-listbox" className="relative mt-1">
                    <button
                        type="button"
                        className={`w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm text-left focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`}
                        onClick={toggleListbox}
                        aria-expanded={isOpen}
                    >
                        <span>{selectedValue}</span>
                        <span className="float-right">
                            <i className={`fa ${isOpen ? 'fa-chevron-up' : 'fa-chevron-down'}`}></i>
                        </span>
                    </button>
                    
                    {isOpen && (
                        <div className="absolute z-10 mt-1 w-full bg-white shadow-lg rounded-md border border-gray-300 max-h-60 overflow-auto">
                            {options.map((option) => (
                                <div
                                    key={option}
                                    className={`px-3 py-2 hover:bg-blue-50 cursor-pointer ${selectedValue === option ? 'bg-blue-100' : ''}`}
                                    onClick={() => handleOptionClick(option)}
                                >
                                    {option}
                                </div>
                            ))}
                        </div>
                    )}
                </div>
            );
        };

        // Tower component
        const Tower = ({ disks, index, totalDisks, onDragOver, onDrop, onClick, onDragStart, mode, selectedTower, hintMove }) => {
            const MIN_TOWER_HEIGHT = 400;
            const calculatedHeight = (totalDisks * DISK_HEIGHT) + 40;
            const isMobile = window.innerWidth < 768;
            const towerHeight = isMobile ? calculatedHeight : Math.max(MIN_TOWER_HEIGHT, calculatedHeight);
            
            return (
                <div
                    data-testid={`tower-${index}`} 
                    className="relative flex flex-col items-center justify-end w-full md:w-1/3 mb-4 md:mb-0 ml-2"
                    style={{ height: `${towerHeight}px` }}
                    onDragOver={onDragOver}
                    onDrop={(e) => onDrop(e, index)}
                    onClick={() => onClick(index)}
                >
                    <div className="absolute bottom-0 w-full h-2 tower-base" />
                    <div className="absolute bottom-2 w-2 tower-rod" style={{ height: `${towerHeight - 8}px` }} />
                    {disks.map((disk, diskIndex) => (
                        <div
                            key={diskIndex}
                            data-testid="hanoi-disk"
                            className={`absolute transition-all duration-500 ease-in-out rounded-md disk-shadow ${
                                mode === "manual" ? "cursor-move" : ""
                            } ${
                                selectedTower === index && diskIndex === disks.length - 1
                                    ? "ring-2 ring-blue-500 ring-offset-2"
                                    : ""
                            }`}
                            style={{
                                width: `${(disk / totalDisks) * 90}%`,
                                height: `${DISK_HEIGHT}px`,
                                backgroundColor: DISK_COLORS[disk % DISK_COLORS.length],
                                bottom: `${diskIndex * DISK_HEIGHT + 8}px`,
                                border:
                                    hintMove && hintMove.from === index && diskIndex === disks.length - 1
                                        ? "2px solid yellow"
                                        : "none",
                            }}
                            draggable={mode === "manual"}
                            onDragStart={(e) => onDragStart(e, index, diskIndex)}
                        >
                            <div 
                                className="absolute inset-0 flex items-center justify-center font-bold text-white text-shadow"
                                style={{
                                    fontSize: `${Math.max(12, Math.min(16, DISK_HEIGHT * 0.6))}px`,
                                    textShadow: '1px 1px 2px rgba(0,0,0,0.8)'
                                }}
                            >
                                {disk}
                            </div>
                        </div>
                    ))}
                    <div className="absolute bottom-[-24px] text-center w-full font-medium">
                        {String.fromCharCode(65 + index)}
                    </div>
                </div>
            );
        };

        // Controls component
        const Controls = ({ 
            disks, setDisks, speed, setSpeed, mode, setMode, 
            isPlaying, startVisualization, getHint, resetTowers, 
            totalMoves, message, t 
        }) => {
            const diskOptions = useMemo(() => Array.from({ length: MAX_DISKS - 1 }, (_, i) => i + 2), []);
            const minMoves = 2 ** disks - 1;

            return (
                <div className="w-full md:w-1/5 p-4 flex flex-col space-y-4">
                    <h2 className="text-2xl font-bold">{t("settings")}</h2>
                    
                    <div className="relative">
                        <label className="block text-sm font-medium text-gray-700">{t("numberOfDisks")}</label>
                        <CustomListbox
                            value={disks}
                            onChange={(newValue) => setDisks(newValue)}
                            options={diskOptions}
                            disabled={isPlaying}
                        />
                    </div>
                    
                    <div>
                        <label className="block text-sm font-medium text-gray-700">{t("speed")}</label>
                        <input
                            type="range"
                            min="1"
                            max="1000"
                            value={speed}
                            onChange={(e) => setSpeed(Number(e.target.value))}
                            className="w-full mt-2"
                            disabled={mode === "manual"}
                        />
                        <span className="text-sm text-gray-600">{speed}ms</span>
                    </div>
                    
                    <div>
                        <label className="block text-sm font-medium text-gray-700">{t("mode")}</label>
                        <select
                            data-testid="mode-select"
                            value={mode}
                            onChange={(e) => setMode(e.target.value)}
                            className="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
                        >
                            <option value="manual">{t("manual")}</option>
                            <option value="auto">{t("auto")}</option>
                        </select>
                    </div>
                    
                    {mode === "auto" && (
                        <button
                            onClick={startVisualization}
                            disabled={isPlaying}
                            data-testid="start-auto-button"
                            className="px-4 py-2 font-bold text-white bg-blue-500 rounded hover:bg-blue-600 focus:outline-none focus:shadow-outline disabled:opacity-50"
                        >
                            {totalMoves > 0 ? t("continue") : t("start")}
                        </button>
                    )}
                    
                    {mode === "manual" && (
                        <button
                            onClick={getHint}
                            className="px-4 py-2 font-bold text-white bg-green-500 rounded hover:bg-green-600 focus:outline-none focus:shadow-outline"
                        >
                            {t("getHint")}
                        </button>
                    )}
                    
                    <button
                        onClick={resetTowers}
                        className="px-4 py-2 font-bold text-white bg-red-500 rounded hover:bg-red-600 focus:outline-none focus:shadow-outline"
                    >
                        {t("reset")}
                    </button>
                    
                    <div className="p-2 bg-gray-100 rounded">
                        <label className="block text-sm font-medium text-gray-700">
                            {t("movesCount", { total: totalMoves, minimum: minMoves })}
                        </label>
                    </div>
                    
                    {message && (
                        <div className="mt-4 p-2 bg-yellow-100 text-yellow-800 rounded border border-yellow-200">
                            {message}
                        </div>
                    )}
                </div>
            );
        };

        // Main Hanoi Tower component
        const HanoiTower = () => {
            // Game state
            const [disks, setDisks] = useState(6);
            const [speed, setSpeed] = useState(500);
            const [towers, setTowers] = useState([]);
            const [isPlaying, setIsPlaying] = useState(false);
            const [totalMoves, setTotalMoves] = useState(0);
            const [mode, setMode] = useState("manual");
            const [message, setMessage] = useState("");
            const [hintMove, setHintMove] = useState(null);
            const [selectedTower, setSelectedTower] = useState(null);
            const [language, setLanguage] = useState('zh');

            // Simple translation function
            const t = (key, replacements = {}) => {
                let text = translations[language][key] || key;
                Object.keys(replacements).forEach(
                    (key) => text = text.replace(`{{${key}}}`, replacements[key])
                );
                return text;
            };

            // Reset game state
            const resetTowers = useCallback(() => {
                setTowers([Array.from({ length: disks }, (_, i) => disks - i), [], []]);
                setIsPlaying(false);
                setTotalMoves(0);
                setMessage("");
                setHintMove(null);
                setSelectedTower(null);
            }, [disks]);

            // Initialize towers when disks change
            useEffect(() => {
                resetTowers();
            }, [disks, resetTowers]);

            // Find next optimal move using recursive algorithm
            const findNextMove = useCallback((h, t, conf) => {
                if (h > 0) {
                    const f = conf[h - 1];
                    if (f !== t) {
                        const r = 3 - f - t;
                        const move = findNextMove(h - 1, r, conf);
                        if (move) return move;
                        return { disk: h - 1, from: f, to: t };
                    }
                    return findNextMove(h - 1, t, conf);
                }
                return null;
            }, []);

            // Convert towers to configuration format
            const towersToConf = useCallback(
                (towers) => {
                    const conf = new Array(disks).fill(0);
                    towers.forEach((tower, tIndex) => {
                        tower.forEach((disk) => {
                            conf[disk - 1] = tIndex;
                        });
                    });
                    return conf;
                },
                [disks]
            );

            // Get hint for next move
            const getHint = useCallback(() => {
                const conf = towersToConf(towers);
                const move = findNextMove(disks, 2, conf);
                if (move) {
                    setHintMove(move);
                    setMessage(
                        t("hintMessage", { 
                            from: String.fromCharCode(65 + move.from), 
                            to: String.fromCharCode(65 + move.to) 
                        })
                    );
                } else {
                    setHintMove(null);
                    setMessage(t("noHintAvailable"));
                }
            }, [towers, disks, findNextMove, towersToConf, t]);

            // Start automatic visualization
            const startVisualization = useCallback(() => {
                setIsPlaying(true);
                setMessage("");
            }, []);

            // Handle mode change
            const handleModeChange = (newMode) => {
                setMode(newMode);
                if (newMode === "manual") {
                    setIsPlaying(false);
                }
            };

            // Auto-play effect
            useEffect(() => {
                if (mode === "auto" && isPlaying) {
                    const timer = setTimeout(() => {
                        const conf = towersToConf(towers);
                        const move = findNextMove(disks, 2, conf);
                        
                        if (move) {
                            setTowers((prev) => {
                                const newTowers = prev.map((tower) => [...tower]);
                                const disk = newTowers[move.from].pop();
                                newTowers[move.to].push(disk);
                                return newTowers;
                            });
                            setTotalMoves((prev) => prev + 1);
                            setMessage("");
                        } else {
                            setIsPlaying(false);
                            setMessage(t("gameCompleted"));
                        }
                    }, speed);

                    return () => clearTimeout(timer);
                }
            }, [mode, isPlaying, towers, speed, disks, findNextMove, towersToConf, t]);

            // Drag and drop handlers
            const handleDragStart = (e, fromTower, diskIndex) => {
                if (mode !== "manual") return;
                if (diskIndex !== towers[fromTower].length - 1) {
                    e.preventDefault();
                    return;
                }
                e.dataTransfer.setData("text/plain", JSON.stringify({ 
                    fromTower, 
                    diskSize: towers[fromTower][diskIndex] 
                }));
            };

            const handleDragOver = (e) => {
                e.preventDefault();
            };

            const handleDrop = (e, toTower) => {
                e.preventDefault();
                if (mode !== "manual") return;

                const data = JSON.parse(e.dataTransfer.getData("text/plain"));
                const { fromTower, diskSize } = data;

                if (fromTower === toTower) return;

                const targetTower = towers[toTower];
                if (targetTower.length === 0 || diskSize < targetTower[targetTower.length - 1]) {
                    setTowers((prev) => {
                        const newTowers = prev.map((tower) => [...tower]);
                        const disk = newTowers[fromTower].pop();
                        newTowers[toTower].push(disk);
                        return newTowers;
                    });
                    setTotalMoves((prev) => prev + 1);
                    setMessage(t("moveSuccess"));
                    setHintMove(null);

                    // Check if game is completed
                    if (towers[2].length === disks - 1 && toTower === 2) {
                        setMessage(t("gameCompleted"));
                    }
                } else {
                    setMessage(t("moveFailure"));
                }
            };

            // Handle tower clicks for manual movement
            const handleTowerClick = (towerIndex) => {
                if (mode !== "manual") return;

                if (selectedTower === null) {
                    if (towers[towerIndex].length > 0) {
                        setSelectedTower(towerIndex);
                    }
                } else {
                    const fromTower = selectedTower;
                    const toTower = towerIndex;
                    const diskSize = towers[fromTower][towers[fromTower].length - 1];

                    if (fromTower !== toTower) {
                        if (towers[toTower].length === 0 || diskSize < towers[toTower][towers[toTower].length - 1]) {
                            setTowers((prev) => {
                                const newTowers = prev.map((tower) => [...tower]);
                                const disk = newTowers[fromTower].pop();
                                newTowers[toTower].push(disk);
                                return newTowers;
                            });
                            setTotalMoves((prev) => prev + 1);
                            setMessage(t("moveSuccess"));
                            setHintMove(null);

                            // Check if game is completed
                            if (towers[2].length === disks - 1 && toTower === 2) {
                                setMessage(t("gameCompleted"));
                            }
                        } else {
                            setMessage(t("moveFailure"));
                        }
                    }
                    setSelectedTower(null);
                }
            };

            return (
                <div className="flex flex-col">
                    {/* Language selector */}
                    <div className="flex justify-end mb-4">
                        <button
                            onClick={() => setLanguage(language === 'en' ? 'zh' : 'en')}
                            className="px-3 py-1 text-sm bg-gray-200 hover:bg-gray-300 rounded"
                        >
                            {language === 'en' ? '中文' : 'English'}
                        </button>
                    </div>

                    <div className="flex flex-col md:flex-row">
                        <div className="w-full md:w-4/5 p-4 bg-gray-100 rounded-lg">
                            <div className="flex flex-col md:flex-row justify-between h-full">
                                {towers.map((tower, index) => (
                                    <Tower 
                                        key={index} 
                                        disks={tower} 
                                        index={index} 
                                        totalDisks={disks}
                                        onDragOver={handleDragOver}
                                        onDrop={handleDrop}
                                        onClick={handleTowerClick}
                                        onDragStart={handleDragStart}
                                        mode={mode}
                                        selectedTower={selectedTower}
                                        hintMove={hintMove}
                                    />
                                ))}
                            </div>
                        </div>
                        
                        <Controls 
                            disks={disks}
                            setDisks={setDisks}
                            speed={speed}
                            setSpeed={setSpeed}
                            mode={mode}
                            setMode={handleModeChange}
                            isPlaying={isPlaying}
                            startVisualization={startVisualization}
                            getHint={getHint}
                            resetTowers={resetTowers}
                            totalMoves={totalMoves}
                            message={message}
                            t={t}
                        />
                    </div>
                </div>
            );
        };

        // Render the app
        ReactDOM.render(<HanoiTower />, document.getElementById('hanoi-app'));
    </script>
</body>
</html>